#ifndef INPUTBINDER_H
#define INPUTBINDER_H

#include <core/IInput.h>
//#include <core/Events.h>
#include <utils/Maths.h>

// Purpose: Couples input and events in a key-binding system.
//          This class maps specific input (e.g., 'a') to an event (e.g., MoveEvent(LEFT)).
//          It is no more general than simply binding physical keys (and\or other input) to events.
// Status: Mostly complete, needs comment cleaning and some discussion.

// NOTE: This is crap! It can't handle binding more than one event to a single key!
//       Should be replaced with an event listener system!

//KeyDown('A') --> read by IInput (level 1) --> passed into bindings --> event
//Key binding code is level 1
//Using the key binding code is level 2

//Example key bindings
//--------------------
//Action	Key1	Key2
//--------------------
//Left		A	Left
//Right		D	Right
//Jump		Space	-
//--------------------
//Action	Key1	Key2
//--------------------
//Charge	Mouse1	LCONTROL
//Pause		P	-
//Main Menu	Escape	-

//TODO: Implement binding interfaces BUT PROBABLY IN A SEPARATE CLASS
//virtual void bindEventToKeyDown( T_InputEventType* pEvent, Key kPrimaryKey, Key kSecondaryKey ) = 0;	
//Also have continuous input, e.g., mouse position

//NOTE: The T_Event concept must have a void activate() method.

template< class T_Event >
class InputBinder
{
    typedef IInput::Key Key;
    
    IInput* m_pInput;
    
    struct KeyBinding {
        T_Event* pDownEvent;
        T_Event* pUpEvent;
        //T_Event* pPressEvent;
    };
    
    KeyBinding m_KeyBindings[IInput::MAX_KEYS];
    T_Event*   m_pBoundMotionEvent;
    
    
    Scalar m_fMinorMotionTolerance;
    
public:
    InputBinder( IInput* pInput )
	 : m_pInput(pInput), m_fMinorMotionTolerance(0)
    {
        //Initialise the key bindings to nothing
        for ( int k = 0; k != IInput::MAX_KEYS; ++k ) {
            m_KeyBindings[k].pDownEvent = NULL;
            m_KeyBindings[k].pUpEvent = NULL;
            //m_Bindings[k].pPressEvent = 0;
        }
        
        //Initialise the motion events to nothing
        m_pBoundMotionEvent = NULL;
    }
    
    void update()
    {
        //Check each key for down, up and press events, and if a corresponding
        // event has been bound, call it.
        for ( int k = 0; k != IInput::MAX_KEYS; ++k ) {
            if ( m_pInput->getKeyDown( k ) && m_KeyBindings[k].pDownEvent )
                    m_KeyBindings[k].pDownEvent->activate();
            else if ( m_pInput->getKeyUp( k ) && m_KeyBindings[k].pDownEvent )
                    m_KeyBindings[k].pUpEvent->activate();
            //if ( m_pInput->getKeyPress( k ) && m_KeyBindings[k].pPressEvent )
            //        m_KeyBindings[k].pPressEvent->activate();
        }
        
        //Check for motion events
        if ( m_pInput->getMouseMoveDistance() > m_fMinorMotionTolerance
		     && m_pBoundMotionEvent )
        	m_pBoundMotionEvent->activate();
        
    }
    
    //Sets the amount of motion required before a motion event is raised
    void setMinorMotionTolerance( Scalar fVal )
	                                         { m_fMinorMotionTolerance = fVal; }
    
    //Key binding methods
    inline void bindKeyDownToEvent( Key k, T_Event* e )
                                            { m_KeyBindings[k].pDownEvent = e; }
    inline void bindKeyUpToEvent( Key k, T_Event* e )
                                              { m_KeyBindings[k].pUpEvent = e; }
    //inline void bindKeyPressToEvent( Key k, T_Event* e )
    //            { m_Bindings[k].pPressEvent = e; }
    inline void bindMotionToEvent( T_Event* e ) { m_pBoundMotionEvent = e; }
    
};

#endif //INPUTBINDER_H
